package Control;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;

import spread.AdvancedMessageListener;
import spread.SpreadConnection;
import spread.SpreadException;
import spread.SpreadGroup;
import spread.SpreadMessage;

import Flight.ArrivalFlight;
import Flight.DepartureFlight;
import Flight.Flight;
import Mensajes.MensajeEstado;
import Mensajes.MensajeEstado_Terminal;
import Operaciones.Constantes;
import Operaciones.Ficheros;

public class Terminal extends Observable implements AdvancedMessageListener{
		
		private List<Flight> vuelosDeparture;
		private List<Flight> vuelosArrival;

		// Parametros de conexion
		private String nombreConexion;
		private int puerto;
		
		private SpreadConnection connection;
		private SpreadGroup groupLlegadas;
		private SpreadGroup groupSalidas;
		private SpreadGroup groupTerminal;

		public Terminal (String pnombreConexion, int ppuerto){
			// Establecemos el nombre de la conexion.
			this.nombreConexion = pnombreConexion;
			this.puerto = ppuerto;
			
			// Vamos a mantener la lista de vuelos en la terminal.
			this.vuelosDeparture = Ficheros.vuelosSalidas(Constantes.nameFileDeparture);
			this.vuelosArrival = Ficheros.vuelosLlegadas(Constantes.nameFileArrival);
			
			// Ordenamos las listas.
			Collections.sort(this.vuelosDeparture);
			Collections.sort(this.vuelosArrival);
		}
		
		public void conexionSpread (){
			/////////////////////////////////////////////////////
			//Creando la conexion de Spread
			/////////////////////////////////////////////////////
			connection = new SpreadConnection();
			try {
				connection.connect(null, this.puerto, this.nombreConexion, false, true);
				this.connection.add(this);
				System.out.println("Conectado con Spread");
			} catch (SpreadException e1) {
				System.err.println("Problema al crear la conexion con Spread");
			}
			
			groupLlegadas = new SpreadGroup();
			groupSalidas = new SpreadGroup();
			groupTerminal = new SpreadGroup();

			try {
				
				// Nos unimos a los dos grupos de ventanas.
				groupLlegadas.join(connection, Constantes.GRUPO_LLEGADAS);
				groupSalidas.join(connection, Constantes.GRUPO_SALIDAS);
				groupTerminal.join(connection, Constantes.GRUPO_TERMINAL);
				
			} catch (SpreadException e1) {				
				Constantes.mensajeError("Problema al unirse al grupo de Spread");
				System.exit(-1);
			}
			
			/////////////////////////////////////////////////////
			//Fin conexion de Spread
			/////////////////////////////////////////////////////
		}
		
		public static void main (String args[]){
			String nombrePanel = "";
			int puerto = 0;
			try{
				// Cogemos el nombre del panel.
				nombrePanel = args[0];
				// Cogemos el argumento que será el puerto.
				puerto = Integer.parseInt(args[1]);
			}catch (NumberFormatException e ){
				Constantes.mensajeError("No válido el puerto introducido");
				System.exit(-1);
			}catch (java.lang.ArrayIndexOutOfBoundsException e ){
				Constantes.mensajeError("Debes de intrucir como argumento el puerto de ejecución");
				System.exit(-1);
			}
			
			PanelTerminal pt = new PanelTerminal(nombrePanel, puerto);
			new VentanaTerminal(pt, "Terminal - " + nombrePanel);
		}

		
		/////////////////////////////////////////////////////////////////////////////////
		// Implementamos los metodos necesarios para la interfaz AdvancedMessageListener
		/////////////////////////////////////////////////////////////////////////////////

		public void regularMessageReceived(SpreadMessage msg){
			// Extraemos su identificador y si comienza por -- ter -- será un terminal
			try {
				String nameMsg = msg.getSender().toString().split("#")[1];
				if (nameMsg.startsWith("TER") && (msg.getObject() instanceof MensajeEstado_Terminal)){
						MensajeEstado_Terminal msgEstado = (MensajeEstado_Terminal) msg.getObject();
						// Actualizamos las listas de vuelos.
						this.setListArrival(msgEstado.getVuelosArrival());
						this.setListDeparture(msgEstado.getVuelosDeparture());
						
						setChanged();
						notifyObservers(Constantes.GRUPO_LLEGADAS);	
				}
			} catch (SpreadException e) {
				Constantes.mensajeError("Problema al recibir mensaje regular en Terminal");
			}
		}

		public void membershipMessageReceived(SpreadMessage msg){
			if (msg.getMembershipInfo().isCausedByJoin()){
				String nameMsg = msg.getMembershipInfo().getJoined().toString().split("#")[1];
				// Si el mensaje es causado por una conexion tendré que añadir a la lista de monitores.
				if (msg.getMembershipInfo().getGroup().toString().equals(Constantes.GRUPO_LLEGADAS) && (!(nameMsg.equals(this.nombreConexion)))){
					this.sendMessage_Arrival();
				} else if (msg.getMembershipInfo().getGroup().toString().equals(Constantes.GRUPO_SALIDAS) && (!(nameMsg.equals(this.nombreConexion)))) {
					this.sendMessage_Departure();
				} else if (msg.getMembershipInfo().getGroup().toString().equals(Constantes.GRUPO_TERMINAL) && (!(nameMsg.equals(this.nombreConexion)))){
					this.sendMessage_Terminal();
				} else if (msg.getMembershipInfo().getGroup().toString().equals(Constantes.GRUPO_PUNTO) && (!(nameMsg.equals(this.nombreConexion)))){
					this.sendMessage_Terminal();
				}
				
			}	
		}
		//////////////////////////////////////////////////////////
		// Fin de la implementación de los métodos de la interfaz
		//////////////////////////////////////////////////////////
		
		
		/////////////////////////////////////////////////////////////////////
		// Enviar mensajes a diferentes grupos de Spread
		/////////////////////////////////////////////////////////////////////
		public boolean sendMessage (SpreadGroup grupo, String message){
			// Creamos el mensaje de envio
			SpreadMessage msg = new SpreadMessage();
			try {
				// Creamos el Mensaje de estado asociado a este envio.
				MensajeEstado me = new MensajeEstado(message);
				msg.setObject(me);
				msg.setSelfDiscard(true);
				msg.addGroup(grupo);
				connection.multicast(msg);
			} catch (SpreadException e) {
				Constantes.mensajeError("Problema al enviar mensaje de Spread");
				return false;
			}
			return true;
		}
		
		public boolean sendMessage_Terminal (){
			// Creamos el mensaje de envio
			SpreadMessage msg = new SpreadMessage();
			try {
				// Creamos el Mensaje de estado asociado a este envio.
				MensajeEstado_Terminal me = new MensajeEstado_Terminal((ArrayList<Flight>)this.vuelosDeparture,(ArrayList<Flight>)this.vuelosArrival);
				msg.setObject(me);
				msg.setSafe();
				msg.setSelfDiscard(true);
				msg.addGroup(this.groupTerminal);
				connection.multicast(msg);
			} catch (SpreadException e) {
				Constantes.mensajeError("Problema al enviar mensaje al grupo de Terminales");
				return false;
			}
			return true;
		}
		/////////////////////////////////////////////////////////////////////
		// Fin metodos que envia mensajes de Spread
		/////////////////////////////////////////////////////////////////////

		
		/////////////////////////////////////////////////////////////////////
		// Get y Set de la clase
		/////////////////////////////////////////////////////////////////////
		public void setListDeparture(List<Flight> pvuelosDeparture) {
			this.vuelosDeparture = pvuelosDeparture;
		}
		
		public void setListArrival(List<Flight> pvuelosArrival) {
			this.vuelosArrival = pvuelosArrival;
		}
		
		public List<Flight> getListDeparture(){
			return this.vuelosDeparture;
		}
		
		public List<Flight> getListArrival(){
			return this.vuelosArrival;
		}
		
		public SpreadGroup getGroupSalidas(){
			return this.groupSalidas;
		}
		
		public SpreadGroup getGroupLlegadas(){
			return this.groupLlegadas;
		}
		/////////////////////////////////////////////////////////////////////
		// Fin metodos Get y Set de la clase
		/////////////////////////////////////////////////////////////////////
		
		
		
		/////////////////////////////////////////////////////////////////////
		// Metodos para operar con las listas de vuelos
		/////////////////////////////////////////////////////////////////////
		public String showFlight (List<Flight> listaVuelos, String estado){
			boolean mostrar = false;
			String cadena = "";
			Iterator<Flight> it = listaVuelos.iterator();
			
			while (it.hasNext()){
				// Get flight
				Flight f = (Flight) it.next();
				if (f.getStatus().equals(estado)){
					cadena += f.toString() + "\n";
					mostrar = true;
				}
			}
			
			if (!(mostrar))
				return null;
			
			return cadena;
		}
		
		public void show(List<Flight> listaVuelos){
			Iterator<Flight> it = listaVuelos.iterator();
			int count = 0;
			while ((it.hasNext()) && (count <= Constantes.NUM_VUELOS_PANEL)){
				// Get flight
				System.out.println(new Date() + " " + it.next());
				count++;
			}
		}
		
		public boolean changeStatus (List<Flight> listaVuelos, String id, String estadoTiene, String estado){
			Iterator<Flight> it = listaVuelos.iterator();
			int i = 0;
			while (it.hasNext()){
				// Get flight
				Flight f = it.next();
				if (f.getFlightNumber().equals(id) && f.getStatus().equals(estadoTiene)){
					it.remove();
					if (!(estado.equals(Constantes.DESPEGUE) || estado.equals(Constantes.ATERRIZADO))){
						f.setState(estado);
						listaVuelos.add(i,f);
					}
					
					return true;
				}
				i++;
			}
			return false;
		}
		
		public void sendMessage_Departure(){
			String departure_aux = "";
			int count = 0;
			
			Iterator <Flight> vuelosDeparture = this.vuelosDeparture.iterator();
			while (vuelosDeparture.hasNext() && (count < Constantes.NUM_VUELOS_PANEL)){
				DepartureFlight f = (DepartureFlight) vuelosDeparture.next();
				departure_aux += "<html>&nbsp;&nbsp;&nbsp;&nbsp;" + f.getDepartureTime().getTime() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getDestination() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getFlightNumber() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getDesk() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getBoardingTime() + "&nbsp;&nbsp;&nbsp;" + f.getBoardingGate() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getStatus() + "<p>";
				count++;
			}
			
			if (!(sendMessage(this.groupSalidas,departure_aux))){
				Constantes.mensajeError("Error al enviar el mensaje a los grupos de salidas");
			}
			
		}
		
		public void sendMessage_Arrival(){
			String arrival_aux = "";
			int count = 0;
			
			Iterator<Flight> vuelosArivals = this.vuelosArrival.iterator();
			while (vuelosArivals.hasNext() && (count < Constantes.NUM_VUELOS_PANEL)){
				ArrivalFlight f = (ArrivalFlight)vuelosArivals.next();
				arrival_aux += "<html>&nbsp;&nbsp;&nbsp;&nbsp;" + f.getArrivalTime().getTime() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getSource() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getFlightNumber() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getLoungeRoom() + "&nbsp;&nbsp;&nbsp;&nbsp;" + f.getStatus() + "<p>";
				count++;
			}
			
			if (!(sendMessage(this.groupLlegadas,arrival_aux))){
				Constantes.mensajeError("Error al enviar el mensaje a los grupos de llegadas");
			}
		}
		/////////////////////////////////////////////////////////////////////
		// Fin metodos para operar con las listas de vuelos
		/////////////////////////////////////////////////////////////////////
}
